#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;
use File::Basename;
use Getopt::Long;
use Pod::Usage;

my $backup_type=undef;	# Type of Backup to be run (LOG or DB)
my $backup_sid=undef;	# SID of Database
my $generation=undef;
my $flag_help;			# Help Flag

GetOptions("type=s"=>\$backup_type,
		   "sid=s"=>\$backup_sid,
		   "generation"=>\$generation,
		   "help|?"=>\$flag_help);

&pod2usage(verbose=>2) if ($flag_help); # display usage info if help flag is defined		   
&pod2usage(verbose=>0,message=>"please supply backup type and SID") unless ($backup_type or $backup_sid); # display usage info if options are missing

my $nsr_path = ( $^O =~ m/Win/) ? 'C:/Legato/nsr' : '/nsr'; # setting networker path depending on OS
my $config_file= $nsr_path.'/res/nsrMaxDB_'.$backup_sid.'.cfg'; # setting config-file to use
my @pass;

my %options=(
				nr_ignores=>1, # times to ignore LOG run while db backup is running - default is 1 max is 3
				nr_ignores_force=>'', # force parameter to increase nr_ignores up to a max of 12
				nr_wait=>2, # wait x * 5min , max. 1 hours - default is 10 minutes - time to wait db backup for running log backup
				user=>'', # user for connection to database
				pwd=>'', # password for user
				dbmcli_bin=>'', # path of dbmcli
				nsr_env_path=> $nsr_path.'/res', # environment files path
				maxdb_env=> $nsr_path.'/res/env_'.$backup_sid, # environment file for maxdb
				maxdb_env_backup=> $nsr_path.'/res/env_'.$backup_type.'_'.$backup_sid, # environment template file for db backup
				maxdb_env_backup_generation=> $nsr_path.'/res/env_'.$backup_type.'_'.$backup_sid.'_Generation', # environment template file for db-generation backup				
				nsr_log_file=> $nsr_path.'/applogs/nsr_'.$backup_sid.'.log', # networker logfile
				lck_log_file=> $nsr_path.'/tmp/LOG_'.$backup_sid.'_run.lck',  # lock file for log backup
				lck_db_file=> $nsr_path.'/tmp/DB_'.$backup_sid.'_run.lck',  # lock file for db backup
				lck_run_file=> $nsr_path.'/tmp/LOG_'.$backup_sid.'_runs_check.lck', # lock file for log backup counter
				lck_backup_file=> $nsr_path.'/tmp/'.$backup_type.'_'.$backup_sid.'_run.lck', # lock file for defined backup_type
				error_nr=>2 # error-code to be returned - default is higher than 0
				  
);

############################### SUB's ###############################

# sub for reading of configured parameters in config file; paths are mandatory, retentions are optional
sub read_parameter_file(){

my $read_parameter_file=0; # set default return value for sub
open(PARAMETER_FILE,"<$config_file") or die "could not open parameterfile $config_file: $!"; # open config file
	my @parameters=<PARAMETER_FILE>; # read paramters from configfile
	chomp @parameters; # cut \n
close(PARAMETER_FILE); # close file

foreach (@parameters){ # read each line in config file
	next if ($_ =~ m/^$/ || $_ =~ m/^#/); # skip empty line or line begin with # = comment
	my($key,$value)=split(/\s*=\s*/,$_,2); # split line into key and value, from form key = value 
	if ($key ne "pwd"){$value=~ s/\s*#.*//g if ($value =~ m/#/);} # check if line contains comment # - and cut comment only if line does not contain password
	$options{$key}=$value; # set key and value in hash
}
if ((not -e $options{dbmcli_bin}) or (not $options{user}) or (not $options{pwd})){
	&logging ("faulty/missing entries in cfg-file - please check");$options{error_nr} = 5;
	&logging ("-" x 40); # logfile entry spacer line
	exit $options{error_nr} # exit with predefined error_nr 2
}
@pass=unpack("C*",$options{pwd});
foreach (@pass){$_--;}
return $read_parameter_file; # return sub status 0 = ok, 1 = not ok (abort flag)
}  

# sub for message logging - messages are print to command line and log file
sub logging {
 my $log_message = join(' ',@_); 
 open (LOG,">>$options{nsr_log_file}") or die "could not open log file $options{nsr_log_file}\n"; # open log-file
 print localtime()." ".$log_message."\n";	# print message with timestamp onto STDOUT (into commandline for manual started backups)
 print LOG localtime()." ".$log_message."\n"; # print message with timestamp into log-file
 close (LOG) or die "could not close log file $options{nsr_log_file}\n"; # close log-file
}

############################### MAIN ###############################
if ($generation){$options{maxdb_env_backup}=$options{maxdb_env_backup_generation};}
&logging ("-" x 40); # logfile entry spacer line
if (not $backup_type or (($backup_type  ne "LOG") and ($backup_type ne "DB"))){ #check if input is valid or not
	&logging ("Backup Type not defined - exiting"); # logfile entry
	&logging ("-" x 40); # logfile entry spacer line
	exit $options{error_nr}} # exit with predefined error_nr 2

read_parameter_file(); # read entries from parameter file
my $strg=pack("C*",@pass);
$options{pwd}=reverse(substr(lcfirst($strg),0,length($strg)/2)).lcfirst(reverse(substr($strg,length($strg)/2)));
my $maxdb_usr_connect = "-u $options{user},$options{pwd}"; # connection string user,password
my $maxdb_util_connect = "-uUTL $options{user},$options{pwd}"; # connection string utility session user,password

umask 0111; # setting umask thus file permission is 666
my $db_connection = "$maxdb_usr_connect $maxdb_util_connect"; # putting together whole connection string
my $backup_command_type="LOG";
if ($backup_type eq "DB"){$backup_command_type="DATA"}

my $backup_command= "$options{dbmcli_bin} -d $backup_sid $db_connection backup_start NSR_$backup_type"."_"."$backup_sid $backup_command_type"; # putting together backup command

my $backup_command_msg = $backup_command; # backup command for output message
$backup_command_msg =~ s/$db_connection //g; # deleting connection information in backup command message - required for logging

$options{error_nr} = 0; # set error counter for exit
if ($options{nr_ignores} > 3) {$options{nr_ignores}=3;} # check for ignore times of log backup while db backup is running - max. 3 is set

if ($options{nr_ignores_force}){$options{nr_ignores_force} >=12 ? $options{nr_ignores}=12 : $options{nr_ignores}=$options{nr_ignores_force};} # nr_ignores_force used to overwrite nr_ignores

if ($options{nr_wait} > 12) {$options{nr_wait}=12;} # check for wait time max. 1 hour is set


&logging ("start of $backup_type backup run for $backup_sid"); # logfile entry

############################### LOG / DB Backup ###############################

if (-e $options{lck_backup_file}) {	# check if lck_backup_file (backup is running) exists and abort if still running
	&logging ("$backup_type backup still running or is being aborted");
	&logging ("if backup is not running delete '$options{lck_backup_file}'");
	$options{error_nr} = 1141;
}
open (LCK,">$options{lck_backup_file}") or die "could not open lck file $options{lck_backup_file}\n"; # create lck-file

if ($backup_type eq "LOG"){	# check if db backup is running - if not try to start log backup
	if (-e $options{lck_db_file}) {
 		my $runs=0; # setting current runs to 0
 		if (-e $options{lck_run_file}) { # check if log file for log backup counter exist if yes then read counter
  			open (RUNS,"<$options{lck_run_file}") or die "could not open lck file $options{lck_run_file}\n"; # open log file for counter
  			$runs = <RUNS>; # read counter
  			close (RUNS); # close log file
 		}
 		$runs += 1; # setting runs 1 higher
 		open (RUNS,">$options{lck_run_file}") or die "could not open lck file $options{lck_run_file}\n"; # open log file for counter for write
 		&logging ("DB Backup is running ignoring LOG Backup $runs times"); # log files entry
 
 		# check if run higher than allowed ignores - if so abort 
 		if ($runs <= $options{nr_ignores}){ 
   			$options{error_nr} = 0; # counter is lower than allowed - setting error nr to 0 - ignore backup and end script successfull
   		} else { # counter is to high - abort backup
   			&logging ("if DB backup is not running delete '$options{lck_db_file}'"); # log file entry
   			$options{error_nr} = 1140; # setting error nr for script abort
 		}
 		print RUNS "$runs"; # print new counter to log file for counter
 		close (RUNS); # close log file for counter
 		&logging ("end of $backup_type backup run for $backup_sid , RC: $options{error_nr}\n"); # logfile entry
   		exit $options{error_nr}; # exit with error code depending on result
	} 
}

if ($backup_type eq "DB"){	# check if log backup is running and wait specific time
	my $run=1;
	my $waittime=300;
	while (-e $options{lck_log_file} && $run <= $options{nr_wait}) {
 		&logging ("log backup is running - waiting ". ($waittime/60) ." min.");
 		$run++;
 		sleep $waittime;
	}
	if (-e $options{lck_log_file}) { # check if lock file still exist and abort if so else start backup
 		&logging ("wait for ". ($run-1) ." * ". ($waittime/60) ." min. but log backup is still running - aborting db backup");
 		$options{error_nr} = 1140;
	}	
}

if ($options{error_nr}==0){	
	if ($backup_type eq "LOG"){unlink $options{lck_run_file} if (-e $options{lck_run_file});} # deleting run file if available
 	if (copy ($options{maxdb_env_backup},$options{maxdb_env})) { # try to copy env template to env file known by maxdb setting and start backup
  			$! = undef; # unsetting error messages
  			&logging ("copied env-file $options{maxdb_env_backup} to $options{maxdb_env}"); # logfile entry
  			&logging ("starting $backup_type backup of $backup_sid with command: $backup_command_msg"); # logfile entry

  			my $backup_result = `$backup_command`; # running backup command - external usage of dbmcli - getting return message
  			chomp $backup_result; # deleting newline in return message
  			$backup_result =~ s/\s+/ /g; # replace mulitple spaces with one space
 
  			&logging ("$backup_result"); # logfile entry
  
  			$options{error_nr} = 0; # setting error nr to 0
  			if ($backup_result =~ m/error\s-123/) { # check if -123 is in return message - exit with rc 0
   				$backup_result = "no logs for backup available (error -123) thus backup successfull"; # setting result text
  			} elsif (($backup_result =~ m/Returncode\s+0/) or ($backup_result =~ m/OK/)) { # check if RC 0 or "OK" is in return message - exit with rc 0
   				$backup_result = "successfully run $backup_type backup for $backup_sid"; # setting result text
  			} else { # if not RC 0 or -123 then failure and abort
   				$backup_result = "failed to run $backup_type backup for $backup_sid"; # setting result text
   				$options{error_nr} = 2; # setting error nr higher than 0
  			}
  			&logging ("run of dbmcli completed with result: $backup_result"); # logfile entry with result text
 	} else { # failed to copy env template to env file - abort backup
  		&logging ("Failed to copy env-file $options{maxdb_env_backup} to $options{maxdb_env}"); # logfile entry
  		$options{error_nr} = 1; # setting error nr
 	}
	close (LCK); # closing lock file
	unlink $options{lck_backup_file}; # deleting lock file
}
&logging ("end of $backup_type backup run for $backup_sid , RC: $options{error_nr}\n"); # logfile entry
exit $options{error_nr}; # exit with error code depending on result

__END__

=pod

=head1 VERSION 1.0

=head1 NAME nsrMaxDB_backup.pl

=head1 DESCRIPTION

The script is used to backup MaxDB databases (LOG and DB Backup). 
The needed Information are provided via commandline options and parameters from config-file. 
The config-file has to be at following location: 
(Windows: C:\Legato\nsr\res\  -  Unix: /nsr/res) 
and name has to be nsrMaxDB_<SID>.cfg where SID is provided via commandline option.

=head1 SYNOPSIS

B<nsrMaxDB_backup.pl> -t <Backup-Type> -s <SID>

B<nsrMaxDB_backup.pl> -t DB -s <SID> -generation

B<nsrMaxDB_backup.pl> -help

=head1 OPTIONS

=over 4

=item -help      displays help

=back

=cut
